package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
)

var db *sql.DB

type User struct {
	id      int
	balance int
}

type UserMoneyLogger struct {
	id     int
	userID int
	number int
}

func main() {
	dsn := "root:123456@tcp(127.0.0.1:3306)/test"
	var err error
	db, err = sql.Open("mysql", dsn)
	if err != nil {
		panic(err)
	}
	err = db.Ping()
	if err != nil {
		panic(err)
	}

	// 在数据库并发访问中，同一事务内对相同记录执行两次查询，因中间有其他事务修改并提交了该记录，导致两次查询返回不同数据的现象。
	// 活动根据不同积分梯度生成用户名单发奖品
	// 事务a查询积分1000以下送一包餐巾纸，小明积分900，名单中有小明
	// 事务b小明积分900，做任务获得了600，一共1500积分
	// 事务a查询1000到2000送一桶油，生成名单，这样小明收到了2个礼品。
	// 解决方案：第二次查询的记录如果在第一次查询里，user_id not in (第一次查询的user_id集合)
	gift()
}

func gift() {
	db.Exec("SET session transaction_isolation = 'READ-COMMITTED'")
	var isolationLevel string
	var err error
	err = db.QueryRow("SELECT @@transaction_isolation;").Scan(&isolationLevel)
	if err != nil {
		panic(err)
	}
	fmt.Printf("当前事务隔离级别: %s\n", isolationLevel)

	conn1, err := db.Begin()
	if err != nil {
		fmt.Println("begin failed :", err)
		return
	}

	conn2, err := db.Begin()
	if err != nil {
		fmt.Println("begin failed :", err)
		return
	}
	userRows, err := conn1.Query("select id,balance from users where balance < 1000")
	if err != nil {
		panic(err)
	}

	for userRows.Next() {
		var user User
		userRows.Scan(&user.id, &user.balance)
		fmt.Println(user.id, user.balance)
	}

	fmt.Println("conn2更新用户数据")
	conn2.Exec("update users set balance=balance+? where id=?", 500, 1)
	conn2.Commit()
	fmt.Println("conn2提交")

	userRows, err = conn1.Query("select id,balance from users where balance >= 1000")
	if err != nil {
		panic(err)
	}

	for userRows.Next() {
		var user User
		userRows.Scan(&user.id, &user.balance)
		fmt.Println(user.id, user.balance)
	}

	conn1.Commit()
}
